#include <sobjc-commons/st.h>
#include <sobjc-commons/defines.h>
#include <sobjc-commons/str_list.h>
#include "sobjc/runtime.h"
#include "selector.h"
#include "ivar.h"
#include "method.h"

Class
objc_make_class(const char *name,
                Class superclass,
                size_t inst_size,
                IvarList ivars,
                ProtocolList protocols,
                int dtable_size)
{
  Class cls;

  if (superclass)
    dtable_size += superclass->dtable_size;

  cls = (Class)objc_malloc(sizeof(struct objc_class) +
                           sizeof(IMP) * dtable_size);
  cls->name = name;
  cls->superclass = superclass;
  cls->version = 0;
  cls->flags = 0;
  cls->instance_size = inst_size;
  cls->ivars = ivars;
  cls->methods = NULL;
  cls->protocols = protocols;
  cls->subclasses = NULL;
  cls->dtable_size = dtable_size;
  MEMZERO(cls->dtable, IMP, dtable_size);

  return cls;
}

#define METACLASS_SUFFIX    " class"

Class
objc_make_metaclass(Class cls, int dtable_size)
{
  char *name;
  Class metaclass, metaclass_superclass = NULL;

  name = ALLOC_N(char, strlen(cls->name)+strlen(METACLASS_SUFFIX)+1);
  sprintf(name, "%s" METACLASS_SUFFIX, cls->name);
  if (cls->superclass != NULL)
    metaclass_superclass = cls->superclass->isa;
  metaclass = objc_make_class(name, metaclass_superclass,
                              0, NULL, NULL, dtable_size);
  objc_free(name);
  return metaclass;
}

const char *
objc_class_get_name(Class cls)
{
  return cls->name;
}

Class
objc_class_get_metaclass(Class cls)
{
  return cls->isa;
}

Class
objc_class_get_superclass(Class cls)
{
  return cls->superclass;
}

BOOL
objc_class_is_kind_of(Class cls, Class base)
{
  while (cls) {
    if (cls == base)
      return YES;
    cls = cls->superclass;
  }
  return NO;
}

unsigned int
objc_class_get_flags(Class cls)
{
  return cls->flags;
}

size_t
objc_class_get_instance_size(Class cls)
{
  return cls->instance_size;
}

Ivar
objc_class_get_ivar(Class cls, const char *name)
{
  IvarList ivars;

  ivars = cls->ivars;
  while (ivars) {
    if (STREQ(ivars->car->name, name))
      return ivars->car;
    ivars = ivars->cdr;
  }

  return NULL;
}

int
objc_class_get_version(Class cls)
{
  return cls->version;
}

void
objc_class_set_version(Class cls, int version)
{
  cls->version = version;
}

MethodList
objc_class_get_method_list(Class cls)
{
  return cls->methods;
}

Method
objc_class_get_instance_method(Class cls, SEL sel)
{
  MethodList list;

  list = cls->methods;
  while (list != NULL) {
    if (list->car->name == sel)
      return list->car;
    list = list->cdr;
  }
  return NULL;
}

Method
objc_class_get_class_method(Class cls, SEL sel)
{
  return objc_class_get_instance_method(cls->isa, sel);
}

void
objc_class_add_method(Class cls, Method meth, int dcode)
{
  cls->methods = objc_method_list_add(cls->methods, meth);
  meth->meth_class = cls;
  if (dcode >= 0) 
    cls->dtable[dcode] = meth->imp;
}

IMP objc_class_get_imp(Class cls, SEL sel)
{
  Method meth;

  meth = objc_class_get_instance_method(cls, sel);
  if (meth)
    return meth->imp;
  else
    return NULL;
}

void 
objc_class_set_before_method(Class cls, SEL sel, IMP imp)
{
  Method meth;

  meth = objc_class_get_instance_method(cls, sel);
  if (meth != NULL)
    meth->before_imp = imp;
}

void
objc_class_set_after_method(Class cls, SEL sel, IMP imp)
{
  Method meth;

  meth = objc_class_get_instance_method(cls, sel);
  if (meth != NULL)
    meth->after_imp = imp;
}

void
objc_class_set_around_method(Class cls, SEL sel, IMP imp)
{
  Method meth;

  meth = objc_class_get_instance_method(cls, sel);
  if (meth != NULL)
    meth->around_imp = imp;
}

id
objc_class_make_instance(Class cls)
{
  id new;

  new = (id)objc_malloc(cls->instance_size);
  new->isa = cls;

  return new;
}

